/*
	This file is part of Dope.
	
	Dope is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Dope is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Dope.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @file 
 * 
 * Contains functions definitions prototyped in hash.h
 */
#include "hash.h"
/*
 * Assign the base address for a new hash
 */
HASH hashCreate( )
{
	HASH root = (HASH) calloc( sizeof( struct _hash ), 1 );
	
	if( root NOT NULL )
	{
		root->last = NULL ;
		root->key = NULL ;
		root->value = NULL ;
		root->root = TRUE ;
		root->__destructor = NULL;
	}
	else return NULL;
	
	return root ;
}

/*
 * Assign a new element in the hash named key pointing at value
 */
BOOL hashAdd( HASH * hash, PSTR key, CAST value )
{
	if( hashFind( hash, key ) IS NULL )
	{
		if( *hash NOT NULL )
		{	
			HASH next = (HASH) calloc( sizeof( struct _hash ), 1 );
			
			if( next NOT NULL )
			{
				next->key = key;
				next->value = value;
				next->last = *hash;
				
				next->__destructor = NULL;
				
				*hash = next ;
				
				return TRUE;
			}
			else return FALSE;
		}
		else return FALSE;
	}
	else return hashEdit( hash, key, value );
}
/*
 * Assign a new complex element in the hash named key pointing at value
 */
BOOL hashComplex( HASH * hash, PSTR key, CAST value, BOOL (*construct)( HASH *element ), NONE (*destruct)( HASH * element ) )
{
	if( hashFind( hash, key ) IS NULL )
	{
		if( *hash NOT NULL )
			{	
				HASH next = (HASH) calloc( sizeof( struct _hash ), 1 );
				
				if( next NOT NULL )
				{
					next->key = key;
					next->value = value;
					
					if( construct NOT NULL )
					{
						next->__constructor = construct;
						
						if( construct( &next ) IS TRUE )
						{
							next->last = *hash;
							
							next->__destructor = destruct;
										
							*hash = next ;
							
							return TRUE;
						}
					}
					else
					{
						next->last = *hash;
						next->__constructor = NULL;			
						next->__destructor = destruct;
									
						*hash = next ;
						
						return TRUE;
					}
					free( next );
				}
			}
	}
	else return hashEdit( hash, key, value );
	
	return FALSE;
}

/*
 * Update hash element key to point at value
 */
BOOL hashEdit( HASH * hash, PSTR key, CAST value )
{
	HASH search = NULL;
	
	HASH_L_START(hash, search)
	{
		if( strcmp( search->key, key ) IS 0 )
		{
			if( search->__destructor NOT NULL )
			{
				search->__destructor( &search );
			}
			
			if( ( search->value = value ) != NULL )
			{
				if( search->__constructor NOT NULL )
				{
					if( search->__constructor( &search ) IS TRUE )
					{
						return TRUE;
					}
					else return FALSE;
				}
				else return TRUE;
			}
			else return FALSE;
		}
	}
	HASH_L_END(hash, search)
	
	return FALSE;
}
/*
 * Remove key from hash
 */
BOOL hashRemove( HASH * hash, PSTR key )
{
	HASH created = hashCreate();
	HASH search = NULL;
		
	HASH_L_START(hash, search)
	{
		if( strcmp( search->key, key ) NOT 0 )
		{
			if( hashComplex( &created, search->key, search->value, search->__constructor, search->__destructor ) IS FALSE )
			{
				hashDestroy(&created);
				
				return FALSE;
			}		
		}
	}
	HASH_L_END(hash, search);

	hashDestroy(hash);
	
	*hash = created;

	return hashSize(&created) > 0 ? TRUE : FALSE;
}

/*
 * Find element key in hash and return it's value
 */
CAST hashFind( HASH * hash, PSTR key )
{
	HASH search = NULL;
	
	HASH_L_START(hash, search)
	{
		if( strcmp( search->key, key ) IS 0 )
		{
			return search->value ;
		}
	}
	HASH_L_END(hash, search)
	
	return NULL;
}

/*
 * Count total number of elements in hash
 */
MASS hashSize( HASH * hash )
{
	MASS size = 0;
	HASH search = NULL;
		
	HASH_L_START(hash, search)
	{
		size++;
	}
	HASH_L_END(hash, search)
	
	return size;
}

/*
 * Apply callback to every element in hash
 */
BOOL hashApply( HASH * hash, BOOL (*callback)( HASH *, HASH ) )
{	
	HASH element = NULL ;
	
	HASH_L_START(hash, element)
	{
		if( callback( hash, element ) IS FALSE )
		{
			return FALSE;
		}
	}
	HASH_L_END(hash, element)
	
	return TRUE;
}

/*
 * Returns a clone of hash
 */
HASH hashClone( HASH * hash )
{
	HASH copy = hashCreate( );
	
	if( copy NOT NULL )
	{
		HASH element = NULL ;
				
		HASH_L_START(hash, element)
		{
			if( hashComplex( &copy, element->key, element->value, element->__constructor, element->__destructor ) IS FALSE )
			{
				hashDestroy( &copy );
				
				return NULL;
			}
		}
		HASH_L_END(hash, element)
		
		return copy;
	}
	else return NULL;
}
/**
 * Merges contents of merge with hash
 */
BOOL hashMerge( HASH * hash, HASH * merge )
{
	HASH loop = NULL ;
	
	HASH_L_START( merge, loop )
	{
		if( hashComplex( hash, loop->key, loop->value, loop->__constructor, loop->__destructor ) IS FALSE )
		{
			return FALSE;
		}
	}
	HASH_L_END( merge, loop )
	
	return TRUE;
}

/*
 * Release memory associated with hash
 */
NONE hashDestroy( HASH * hash )
{
	HASH search = *hash ;
	HASH next = NULL;
	
	if( search NOT NULL )
	{
		do
		{
			next = search->last ;
			
			if( search->__destructor NOT NULL )
			{
				search->__destructor( &search );
			}
			free( search );
		}
		while( ( search = next ) NOT NULL );
	}
}
